/*
 * DEMElement.h
 *
 *  Created on: 29/gen/2013
 *      Author: lordmzn
 */

#ifndef MOLEM_DEMELEMENT_H_
#define MOLEM_DEMELEMENT_H_

#include "Element.h"
#include <list>

namespace MOLEM {

static const long int DISCRETE_ACC_VALUE = 1;
static const double GRADIENT_DEFAULT_VALUE = 0.0;
static const double GRADIENT_MAX_VALUE = 10000.0;

namespace Direction {
enum SecDirEnum {
	CLOCKWISE = 1,
	THIS = 0,
	COUNTERWISE = -1
};
}

Direction::DirEnum sumDirection(const Direction::DirEnum&,
		const Direction::SecDirEnum&);

/**
 *
 */
class DEMElement: public Element {
public:
	DEMElement(unsigned int xx, unsigned int yy);
	DEMElement(unsigned int xx, unsigned int yy, Direction::DirEnum boundary);
	DEMElement(const DEMElement&);
	~DEMElement();

	bool operator==(const DEMElement&) const;
	bool operator!=(const DEMElement&) const;
	bool operator<(const DEMElement& rv) const;

	void setZvalue(double);
	void markAsSpring();
	// this is for springs
	void findDirection();

	bool isDirectionEvaluated();
	bool isGradientEvaluated();
	bool isElementsAccumulationEvaluated();
	bool isASpring();
	bool isASink();

private:
	/**
	 * Element into which this goes into
	 */
	DEMElement* goesInto; // this->drainsInto
	/**
	 * Secondary direction
	 */
	Direction::SecDirEnum secDir;
	/**
	 * Gradient toward the goesInto Element.
	 */
	double gradient;
	/**
	 * Elements that go into this
	 */
	std::list<DEMElement* > comesFrom;
	/**
	 * Number of cells that go into this.
	 */
	long int elementsAccumulated;
	/**
	 * True if cell has no cell that drains into.
	 */
	bool isSpring;
	/**
	 * True if this cell drains into no cell.
	 */
	bool isSink;

	// this requires the source element
	void findDirection(DEMElement&, Direction::DirEnum);
	/**
	 * Find the steepest direction among the neighbors (D8)
	 * and sets gradient, this->goesInto and goesInto->comesFrom
	 * accordingly
	 */
	Direction::DirEnum findLocalSteepestDirection();
	double findSecondaryDirection(const Direction::DirEnum&);
	/**
	 * just as the name says, no more
	 */
	void setGoesInto(DEMElement&);
	void addDrainsFrom(DEMElement&);
	void markAsSink();
	double evaluateGradient(const DEMElement&) const;
	void setAccumulation(const DEMElement&);
};
}
#endif /* MOLEM_DEMELEMENT_H_ */
